/*
 * MIXMAIN.C Version 1.00 (C)1996 HUDSON SOFT
 * MIX 再生サンプルプログラムメイン(MIXMAIN.C)
 */

#include    <system.h>
#include    <sysboard.h>
#include    <sup.h>
#include    <nt.h>
#include    <king_kr.h>
#include    <soundbox.h>
#include    <scsi.h>
#include    <v93.h>

#include    "cdrscsi.h"

/* 割り込み関連のシンボル定義 */
#define IO_ILR0 0x0e80
#define IO_ILR1 0x0ec0

#define IO_ISR	0x0e00
#define IO_IMR	0x0e40

#define _ISR_AERR   0x80
#define _ISR_TIMR   0x40
#define _ISR_INTEX  0x20
#define _ISR_INTKP  0x10
#define _ISR_INT7A  0x08
#define _ISR_INT72  0x04
#define _ISR_INT7B  0x02
#define _ISR_INTSIO 0x01

#define _IMR_TIMR   0x40
#define _IMR_INTEX  0x20
#define _IMR_INTKP  0x10
#define _IMR_INT7A  0x08
#define _IMR_INT72  0x04
#define _IMR_INT7B  0x02
#define _IMR_INTSIO 0x01

/* HuC6270 関連のシンボル定義 */
#define _7UP_STAT_CR	0x001	/* 衝突検出		*/
#define _7UP_STAT_OR	0x002	/* スプライトオーバー	*/
#define _7UP_STAT_RR	0x004	/* ラスタ検出		*/
#define _7UP_STAT_DS	0x008	/* VRAM-SATB 転送終了	*/
#define _7UP_STAT_DV	0x010	/* VRAM-VRAM 転送終了	*/
#define _7UP_STAT_VD	0x020	/* 垂直帰線期間検出	*/
#define _7UP_STAT_BSY	0x040
#define	_AR_SDMA_READ	0x003
#define	_AR_SDMA_WRITE	0x013

/* 割り込みベクタと割り込みレベルを設定する為の構造体 */
typedef struct {
    void    (*func)();
    int	    vect;
} VECT_INIT ;

/* V-SYNC 割り込みのカウンタ */
unsigned int	vsync_count=0;

/* LEVEL F 割り込み用処理関数（ダミー）
 *
 * LEVEL F 割り込みは HuC6270-B 用割り込み関数 irq_7upB_entry() で処理するので
 * この割り込み関数は使用しません。
 */
void interrupt	irq_err0_entry()
{
}

/* LEVEL E 割り込み用処理関数（ダミー）
 *
 * LEVEL E 割り込みは HuC6272 用割り込み関数 irq_king_entry() で処理するのでこ
 * の割り込み関数は使用しません。
 */
void interrupt	irq_err1_entry()
{
}

/* LEVEL D 割り込み用処理関数（ダミー）
 *
 * LEVEL D 割り込みは HuC6270-A 用割り込み関数 irq_7upA_entry() で処理するので
 * この割り込み関数は使用しません。
 */
void interrupt	irq_err2_entry()
{
}

/* LEVEL C 割り込み用処理関数（ダミー）
 *
 * LEVEL C 割り込みはマスクされ処理されませんが、念の為、処理先を用意し割り込み
 * 処理に登録しておきます。
 */
void interrupt	irq_err3_entry()
{
}

/* LEVEL B 割り込み用処理関数（ダミー）
 *
 * LEVEL B 割り込みはキーパッド用割り込み関数 int_pad() で処理するのでこの割り
 * 込み関数は使用しません。
 */
void interrupt	irq_err4_entry()
{
}

/* LEVEL A 割り込み用処理関数（ダミー）
 *
 * LEVEL A 割り込みはマスクされ処理されませんが、念の為、処理先を用意し割り込み
 * 処理に登録しておきます。
 */
void interrupt	irq_err5_entry()
{
}

/* LEVEL 9 割り込み用処理関数（ダミー）
 *
 * LEVEL 9 割り込みはマスクされ処理されませんが、念の為、処理先を用意し割り込み
 * 処理に登録しておきます。
 */
void interrupt	irq_err6_entry()
{
}

/* LEVEL 8 割り込み用処理関数（ダミー）
 *
 * LEVEL 8 割り込みはマスクされ処理されませんが、念の為、処理先を用意し割り込み
 * 処理に登録しておきます。
 */
void interrupt	irq_err7_entry()
{
}

/* LEVEL F 割り込み用処理関数
 *
 * LEVEL F 割り込みは HuC6270-B からの割り込みを処理します。
 *
 * この割り込み処理では、垂直同期割り込みのカウント、RUN + SELECT でのリセット
 * キーパッド入力リクエストの処理を行ないます。
 *
 * HuC6270-A,HuC6270-B,HuC6272 の各デバイスのアドレスレジスタの待避と復帰は、割
 * 込処理中に（この関数内で）対応するデバイスを使用しない場合は必要ありません。
 *
 * eipc, eipsw の待避と復帰は多重割込を許可しない場合は必用ありません。
 * （この割込処理関数では多重割込を許可していないので削除しても問題ありません）
 */
void interrupt	irq_7upB_entry()
{
    regsave at,ra0-ra3,rg0-rg9,ut;	/* CPU レジスタの退避。復帰は自動 */
    unsigned int	ar_king;	/* HuC6272 の AR レジスタ退避用 */
    unsigned int	ar_7upA;	/* HuC6270-A の AR レジスタ退避用 */
    unsigned int	ar_7upB;	/* HuC6270-B の AR レジスタ退避用 */
    unsigned int	eipc;		/* eipc レジスタの退避用 */
    unsigned int	eipsw;		/* eipsw レジスタの退避用 */
    unsigned int	st;
    static unsigned int	reset_count=0;	/* RUN + SELECT のリセット処理用 */

    ar_king = __INPORTH(IO_KING_AR);	/* HuC6272 の AR レジスタの待避 */
    ar_7upA = __INPORTH(IO_IO_7AR_A);	/* HuC6270-A の AR レジスタの待避 */
    ar_7upB = __INPORTH(IO_IO_7AR_B);	/* HuC6270-B の AR レジスタの待避 */
    eipc    = _EIPC ;			/* eipc レジスタの待避 */
    eipsw   = _EIPSW ;			/* eipsw レジスタの待避 */

    st = __INPORTH(IO_70B_SR);		/* 割り込み要因を取得 */
    if ( (st & _7UP_STAT_VD) != 0 ) {	/* 垂直同期割り込みの場合 */
	vsync_count++;			/* 垂直同期割り込みカウンタを加算 */
	if ( (pad00.KD_DAT & (KP_run|KP_select) ) == (KP_run|KP_select) ) {
	    reset_count++;		/* RUN + SELECT が押されているなら */
	    if ( reset_count==60 ) {	/* 60 回(１秒)押され続けているなら */
		((void (*)())(IRQVEC_SFTRST))();	/* リセットする */
	    }
	} else {
	    reset_count=0;		/* 押されていないならカウンタクリア */
	}
	get_pad();			/* キーパッド入力処理 */
    }
    __OUTPORTH(IO_KING_AR,ar_king);	/* HuC6272 の AR レジスタの復帰 */
    if (ar_7upA == _AR_SDMA_READ) {	/* HuC6270-A の AR レジスタの復帰 */
	__OUTPORTH(IO_70A_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70A_AR, ar_7upA);
    }
    if (ar_7upB == _AR_SDMA_READ) {	/* HuC6270-B の AR レジスタの復帰 */
	__OUTPORTH(IO_70B_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70B_AR, ar_7upB);
    }
    _EIPC  = eipc ;			/* eipc レジスタの復帰 */
    _EIPSW = eipsw ;			/* eipsw レジスタの復帰 */
}

/* LEVEL C 割り込み用処理関数
 *
 * LEVEL C 割り込みは HuC6270-A からの割り込みを処理します。
 *
 * この割り込み処理では、実際には何も処理しません。
 *
 * HuC6270-A,HuC6270-B,HuC6272 の各デバイスのアドレスレジスタの待避と復帰は、割
 * 込処理中に（この関数内で）対応するデバイスを使用しない場合は必要ありません。
 *
 * eipc, eipsw の待避と復帰は多重割込を許可しない場合は必用ありません。
 * （この割込処理関数では多重割込を許可していないので削除しても問題ありません）
 */
void interrupt	irq_7upA_entry()
{
    regsave at,ra0-ra3,rg0-rg9,ut;	/* CPU レジスタの退避。復帰は自動 */
    unsigned int	ar_king;	/* HuC6272 の AR レジスタ退避用 */
    unsigned int	ar_7upA;	/* HuC6270-A の AR レジスタ退避用 */
    unsigned int	ar_7upB;	/* HuC6270-B の AR レジスタ退避用 */
    unsigned int	eipc;		/* eipc レジスタの退避用 */
    unsigned int	eipsw;		/* eipsw レジスタの退避用 */
    unsigned int	st;

    ar_king = __INPORTH(IO_KING_AR);	/* HuC6272 の AR レジスタの待避 */
    ar_7upA = __INPORTH(IO_IO_7AR_A);	/* HuC6270-A の AR レジスタの待避 */
    ar_7upB = __INPORTH(IO_IO_7AR_B);	/* HuC6270-B の AR レジスタの待避 */
    eipc    = _EIPC ;			/* eipc レジスタの待避 */
    eipsw   = _EIPSW ;			/* eipsw レジスタの待避 */

    st = __INPORTH(IO_70A_SR);		/* 割り込み要因を取得 */
    if ( (st & _7UP_STAT_VD) != 0 ) {	/* 垂直同期割り込みの場合 */
					/* 本来ならここで処理するが垂直同期 */
					/* 割り込みは HuC6270-B の割込処理で */
					/* 処理するので、ここでは処理しない */
    }
    __OUTPORTH(IO_KING_AR,ar_king);	/* HuC6272 の AR レジスタの復帰 */
    if (ar_7upA == _AR_SDMA_READ) {	/* HuC6270-A の AR レジスタの復帰 */
	__OUTPORTH(IO_70A_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70A_AR, ar_7upA);
    }
    if (ar_7upB == _AR_SDMA_READ) {	/* HuC6270-B の AR レジスタの復帰 */
	__OUTPORTH(IO_70B_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70B_AR, ar_7upB);
    }
    _EIPC  = eipc ;			/* eipc レジスタの復帰 */
    _EIPSW = eipsw ;			/* eipsw レジスタの復帰 */
}

/* LEVEL E 割り込み用処理関数
 *
 * LEVEL E 割り込みは HuC6272 からの割り込みを処理します。
 *
 * この割り込み処理では SOUND バッファ割り込み、ラスタ監視割り込み、CD SUB-CODE
 * 割り込み、SCSI DMA 終了割り込み、SCSI バスリセット／バスフェーズエラー割り込
 * みを処理します。
 *
 * HuC6270-A,HuC6270-B,HuC6272 の各デバイスのアドレスレジスタの待避と復帰は、割
 * 込処理中に（この関数内で）対応するデバイスを使用しない場合は必要ありません。
 *
 * なお、この割り込み関数では HuC6272 のアドレスレジスタを操作するので、退避と
 * 復帰が必要です。
 *
 * eipc, eipsw の待避と復帰は多重割込を許可しない場合は必用ありません。
 * （この割込処理関数では多重割込を許可していないので削除しても問題ありません）
 */
void interrupt	irq_king_entry()
{
    regsave at,ra0-ra3,rg0-rg9,ut;	/* CPU レジスタの退避。復帰は自動 */
    unsigned int	ar_king;	/* HuC6272 の AR レジスタ退避用 */
    unsigned int	ar_7upA;	/* HuC6270-A の AR レジスタ退避用 */
    unsigned int	ar_7upB;	/* HuC6270-B の AR レジスタ退避用 */
    unsigned int	eipc;		/* eipc レジスタの退避用 */
    unsigned int	eipsw;		/* eipsw レジスタの退避用 */
    unsigned int	st;

    ar_king = __INPORTH(IO_KING_AR);	/* HuC6272 の AR レジスタの待避 */
    ar_7upA = __INPORTH(IO_IO_7AR_A);	/* HuC6270-A の AR レジスタの待避 */
    ar_7upB = __INPORTH(IO_IO_7AR_B);	/* HuC6270-B の AR レジスタの待避 */
    eipc    = _EIPC ;			/* eipc レジスタの待避 */
    eipsw   = _EIPSW ;			/* eipsw レジスタの待避 */

    st = __INPORTH(IO_KING_SRL) ;	/* 割り込み要因の取得 */
    if ( st & 0x00400 ) {		/* SOUND バッファ割り込みの場合 */
	__OUTPORTH( IO_KING_AR, _KS_STAT ) ;
	__INPORTH( IO_KING_DR ) ;	/* 割り込み要因を除去する */
    }
    if ( st & 0x00800 ) {		/* ラスタ監視割り込みの場合 */
					/* 本来ならばここで処理するがラスタ */
					/* 監視割り込みを使用していないので */
					/* ここでは処理しない */
    }
    if ( st & 0x01000 ) {		/* CD SUB-CODE 割り込みの場合 */
					/* 本来ならばここで処理するが CD SUB */
					/* CODE を利用していないのでここでは */
					/* 処理しない */
    }
    if ( st & 0x02000 ) {		/* SCSI DMA 終了割り込みの場合 */
	scsi_dma_int();			/* DMA 転送の後始末を行なう */
	mix_read_int();			/* 次の DMA を起動する */
    }
    if ( st & 0x04000 ) {		/* SCSI バスリセット／バスフェーズ */
					/* エラー割り込みの場合 */
	scsi_block_int();		/* SCSI バス転送の後始末を行なう */
    }
    __OUTPORTH(IO_KING_AR,ar_king);	/* HuC6272 の AR レジスタの復帰 */
    if (ar_7upA == _AR_SDMA_READ) {	/* HuC6270-A の AR レジスタの復帰 */
	__OUTPORTH(IO_70A_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70A_AR, ar_7upA);
    }
    if (ar_7upB == _AR_SDMA_READ) {	/* HuC6270-B の AR レジスタの復帰 */
	__OUTPORTH(IO_70B_AR, _AR_SDMA_WRITE);
    } else {
	__OUTPORTH(IO_70B_AR, ar_7upB);
    }
    _EIPC  = eipc ;			/* eipc レジスタの復帰 */
    _EIPSW = eipsw ;			/* eipsw レジスタの復帰 */
}

/* 割り込みベクタ、割り込みレベル、割り込み優先順位の設定
 *
 * 割り込みベクタ、割り込みレベル、割り込み優先順位の設定を行ないます。
 *
 * 入力：
 * なし
 *
 * 出力：
 * なし
 */
void	init_vect()
{
    VECT_INIT	vect_init[]={		/* 割り込み処理先とレベルの表 */
	irq_7upB_entry, IRQ_LEVELF,
	irq_king_entry, IRQ_LEVELE,
	irq_7upA_entry, IRQ_LEVELC,
	irq_err3_entry, IRQ_LEVELD,
	int_pad,	IRQ_LEVELB,
	irq_err5_entry, IRQ_LEVELA,
	irq_err6_entry, IRQ_LEVEL9,
	irq_err7_entry, IRQ_LEVEL8,
    };
    int	n ;

    for ( n=0 ; n < (sizeof(vect_init)/sizeof(VECT_INIT)) ; n++ ) {
	set_irq_vector( vect_init[n].func, vect_init[n].vect );
					/* 割り込みベクタの設定 */
    }

    __OUTPORTH(IO_IMR,0xffff);		/* すべての割り込みを禁止 */
		    /* 6270-A  6272  6270-B  SIO */
    __OUTPORTH(IO_ILR0,(5<<9)|(6<<6)|(7<<3)|(4<<0));	/* 優先順位設定 */
		    /*        timer  INTEX  INTKP */
    __OUTPORTH(IO_ILR1,	      (1<<6)|(2<<3)|(3<<0));	/* 優先順位設定 */

    __OUTPORTH(IO_IMR,~(_IMR_INTKP|_IMR_INT7A|_IMR_INT72|_IMR_INT7B));
					/* 使用する割り込みのみ解除 */
					/* これ以降、割り込みが発生します */
}

/* 垂直同期待ち
 *
 * 指定した回数の垂直同期を待ち合わします。60 を指定すると約１秒待ち合わせにな
 * ります。
 *
 * 0 を指定した場合は、待ち合わせをせずに復帰しますので注意して下さい。
 *
 * 入力：
 * int		    nums;    待ち合わせ回数
 *
 * 出力：
 * なし
 */
void	vsync_wait(nums)
int	nums;
{
    unsigned int	v;

    v = vsync_count ;
    while( nums > 0 ) {
	while ( v==vsync_count )
	    ;
	v = vsync_count ;
	nums--;
    }
}

/* メインプログラム
 *
 * 各種 I/O デバイスを初期化し、動画再生処理 mix_play() を呼び出します。
 *
 * 入力：
 * なし
 *
 * 出力：
 * なし
 */
void	main()
{
    static struct ADPVOL	adpvol={
	0x3f,				/* ADPCM #1 左チャンネル */
	0,				/* ADPCM #1 右チャンネル */
	0,				/* ADPCM #2 左チャンネル */
	0x3f,				/* ADPCM #2 右チャンネル */
    };
    int	    n;

    init_vect();			/* 割り込みベクタ等の初期化 */
    init_nt();				/* HuC6261 の初期化 */
    set_nt_mode(mode_5M|inter_sync|line263);	/* HuC6261 のモード設定 */
    init_rainbow();			/* HuC6271 の初期化 */
    init_king();			/* HuC6272 の初期化 */
    init_soundbox();			/* HuC6230 の初期化 */
    init_70();				/* HuC6270 の初期化 */

    init_scsi();			/* SCSI の初期化 */
    init_get_pad();			/* キーパッドの初期化 */

    set_70_irqbit(IE_VC,DEV70B);	/* HuC6270 の垂直同期割込を許可 */
    irq_enable();			/* CPU を割り込み受け付け状態に */

    set_kr_trans(KR_TRANS_OFF);		/* HuC6271 <-> HuC6272 間転送を停止 */
    dispon(rain);			/* HuC6271 を表示モードに */

    __OUTPORTH(IO_NT_AR,_NT_CRMY);	/* クロマキーで黒を透過する */
    __OUTPORTH(IO_NT_DR,0x0000);	/* クロマキー Ｙ成分 */
    __OUTPORTH(IO_NT_AR,_NT_CRMU);
    __OUTPORTH(IO_NT_DR,0x8080);	/* クロマキー Ｕ成分 */
    __OUTPORTH(IO_NT_AR,_NT_CRMV);
    __OUTPORTH(IO_NT_DR,0x8080);	/* クロマキー Ｖ成分 */

    __OUTPORTH( IO_NT_AR, _NT_CPA);	/* 全てのパレットを黒にする */
    __OUTPORTH( IO_NT_DR, 0 ) ;
    __OUTPORTH( IO_NT_AR, _NT_CPW);
    for(n=0;n<512;n++){
	__OUTPORTH( IO_NT_DR,0x0088);
    }
					/* ヌルランの指定(黒に) */
    __OUTPORTH(IO_RA_NRLY,0x080);	/* ヌルラン Ｙ成分 */
    __OUTPORTH(IO_RA_NRLU,0x000);	/* ヌルラン Ｕ成分 */
    __OUTPORTH(IO_RA_NRLV,0x000);	/* ヌルラン Ｖ成分 */

    reset_adp(ADP_RESET_ALL);		/* ADPCM のリセット */
    set_adp_liner(SB_ADPCM_ALL,ADP_LINER_ON);	/* ADPCM の直線補間をオンに */
    set_adp_rate(ADPCM_RATE32);		/* ADPCM のサンプルレートの設定 */
    set_adp_vol(&adpvol);		/* ADPCM の音量の設定 */

    mix_play();				/* 動画再生処理を呼び出し */
}
